/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.android.exoplayer2.trackselection;

import android.os.Handler;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.util.Assertions;
import java.util.concurrent.CopyOnWriteArraySet;

/** Selects tracks to be consumed by available renderers. */
public abstract class TrackSelector<T> {

  /**
   * Notified when previous selections by a {@link TrackSelector} are no longer valid.
   */
  public interface InvalidationListener {

    /**
     * Called by a {@link TrackSelector} when previous selections are no longer valid.
     */
    void onTrackSelectionsInvalidated();

  }

  /** Listener of {@link TrackSelector} events. */
  public interface EventListener<T> {

    /**
     * Called when the track selections have changed.
     *
     * @param trackSelections The new track selections.
     */
    void onTrackSelectionsChanged(TrackSelections<? extends T> trackSelections);
  }

  private final Handler eventHandler;
  private final CopyOnWriteArraySet<MappingTrackSelector.EventListener<? super T>> listeners;

  private InvalidationListener listener;
  private TrackSelections<T> activeSelections;

  /**
   * @param eventHandler A handler to use when delivering events to listeners added via {@link
   *     #addListener(EventListener)}.
   */
  public TrackSelector(Handler eventHandler) {
    this.eventHandler = Assertions.checkNotNull(eventHandler);
    this.listeners = new CopyOnWriteArraySet<>();
  }

  /**
   * Registers a listener to receive events from the selector. The listener's methods will be called
   * using the {@link Handler} that was passed to the constructor.
   *
   * @param listener The listener to register.
   */
  public final void addListener(EventListener<? super T> listener) {
    listeners.add(listener);
  }

  /**
   * Unregister a listener. The listener will no longer receive events from the selector.
   *
   * @param listener The listener to unregister.
   */
  public final void removeListener(EventListener<? super T> listener) {
    listeners.remove(listener);
  }

  /** Returns the current track selections. */
  public final TrackSelections<T> getCurrentSelections() {
    return activeSelections;
  }

  /**
   * Initializes the selector.
   *
   * @param listener A listener for the selector.
   */
  public final void init(InvalidationListener listener) {
    this.listener = listener;
  }

  /**
   * Generates {@link TrackSelections} for the renderers.
   *
   * @param rendererCapabilities The {@link RendererCapabilities} of the renderers for which {@link
   *     TrackSelection}s are to be generated.
   * @param trackGroups The available track groups.
   * @return The track selections.
   * @throws ExoPlaybackException If an error occurs selecting tracks.
   */
  public abstract TrackSelections<T> selectTracks(
      RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups)
      throws ExoPlaybackException;

  /**
   * Called when {@link TrackSelections} previously generated by {@link
   * #selectTracks(RendererCapabilities[], TrackGroupArray)} are activated.
   *
   * @param activeSelections The activated {@link TrackSelections}.
   */
  public final void onSelectionActivated(TrackSelections<T> activeSelections) {
    this.activeSelections = activeSelections;
    notifyTrackSelectionsChanged(activeSelections);
  }

  /**
   * Invalidates all previously generated track selections.
   */
  protected final void invalidate() {
    if (listener != null) {
      listener.onTrackSelectionsInvalidated();
    }
  }

  private void notifyTrackSelectionsChanged(final TrackSelections<T> activeSelections) {
    if (eventHandler != null) {
      eventHandler.post(
          new Runnable() {
            @Override
            public void run() {
              for (EventListener<? super T> listener : listeners) {
                listener.onTrackSelectionsChanged(activeSelections);
              }
            }
          });
    }
  }

}
